BACK
MENU

Chess Webpage


Alan Hope, 12 Mar 2023

UCI chess protocol in PHP and vanilla javascript.

I have a UCI interface working on a webpage for Stockfish 15 NNUE and Leela Chess Zero. These are two very strong chess engines. Interacting with an engine using the UCI protocol is a bit tricky involving opening and managing software pipes. Here's my core code thanks to a few helpful websites and a lot of trial and error. You can add your own board and features (that's the fun bit).


Stockfish

The engine comes as an exe file often with some dependency files in the zip. You need to download the version which can run on your software platform and graphics card (for me Windows, lowish graphics-card so CPU version, "stockfish-windows-2022-x86-64-avx2.exe" from the Stockfish website). To check you have a working version double-click on the exe. A window will open. Type "isready" into the window and hit Enter. If it responds "readyok" then you are in business.

AJAX is used to send a command (or comma-separated multiple commands) to the server. The server opens communication with the engine, then reads output until "bestmove" or "readyok" occurs. When either of the keywords is found, the loop finishes and the entire engine response is sent back to the client as a comma separated string.

The client here simply implements the bestmove and then chains the next command. You can pull other information from the engine output easily with some string and array processing of key words. For example the search depth (depth), score in centipawns (cp), or the expected next move (ponder). Other information comes from chess.js which is managing the game. Eg game.game_over()

Step by step

In your web development environment create a new folder and generate 3 files. chess_uci.php, chess_uci_ajax.php, and main.css . Put the chess engine exe file and any required dependencies (eg Leela's neural network weights) in this folder. I have put the javascript inline in chess_uci.php. It works—you can reformat the files later as you prefer.

I used chess.js to track the game and legal moves, this can be conveniently included in your project from a CDN (as below).

chess_uci.php


<?php

$content=<<<HTML
<h1>Chess</h1>
<h2>UCI Interface</h2>

<script>
</script>
HTML;

$webpage=<<<HTML
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="x-ua-compatible" content="ie=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>Chess</title>
    <link rel="stylesheet" href="main.css" />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/chess.js/0.10.2/chess.js" defer></script>
  </head>
  <body>
    {$content}
  </body>
</html>
HTML;

echo($webpage);
?>

chess_uci_ajax.php


<?php

$command = trim($_POST['msg']);
$command = str_replace(',', " \n ", $command) . " \n ";

// define the pipes 0 = stdin, 1 = stdout, 2 = stderr

$descr = array(  0 => array("pipe", "r"),  1 => array("pipe", "w"),  2 => array("pipe", "w") );
$pipes = array();

$process = proc_open("stockfish-windows-2022-x86-64-avx2.exe", $descr, $pipes);
if (is_resource($process)) {
  fwrite($pipes[0], $command);

  $op='';
  while (!feof($pipes[1])) { // read all output from the pipe until keyword found
    $op .= fgets($pipes[1]) . ','; //read 1 line of output, add comma as separator
    if(strpos($op,'bestmove') !== false){
      break;
    }
    if(strpos($op,'readyok') !== false){
      break;
    }
  }
  fclose($pipes[0]);
  fclose($pipes[1]);
  proc_close($process);
  echo json_encode($op); // send back comma-separated output string
}

?>